1 module codegen_d;
2 import defs;
3 import util;
4 
5 void gencode_d(OGLFunctionFamily[] functionFamilies, OGLEnumGroup[] enums, string filename, string modulename, bool isStatic, string containerStruct) {
6 	import std.conv : text;
7 
8 	char[] ret;
9 
10 	// For OpenGL 4.5 output from the "printers" is around 1mb,
11 	//  so 6mb is beyond anything we'll actually need.
12 	// But it'll increase speed enough that it is worth
13 	//  the actual work to get it in code.
14 	ret.reserve(1024 * 1024 * 6); // 1kb * 1mb * 6mb
15 
16 	if (isStatic) {
17 		// seriously what do you expect me to do with this?
18 		containerStruct = null;
19 	}
20 
21 	string prefixContainer, prefix, prefixComment;
22 	if (containerStruct !is null) {
23 		if (isStatic) {
24 			prefixContainer = "    ";
25 			prefix = "        ";
26 		} else {
27 			prefixContainer = "";
28 			prefix = "    ";
29 		}
30 	} else {
31 		prefixContainer = "";
32 		prefix = "    ";
33 	}
34 
35 	prefixComment = prefix ~ " + ";
36 
37 	if (modulename !is null) {
38 		ret ~= "
39 /**
40  * This is a set of OpenGL bindings.
41  *
42  * Generated by ./ogl_gen ....
43  * Do not modify. Regenerate if changes are required.
44  *
45  * Macros:
46  *    D_CODE = <pre><code class=\"D\">$0</code></pre>
47  */\n"[1 .. $];
48 
49 		ret ~= "module ";
50 		ret ~= modulename;
51 		ret ~= ";\n";
52 	} else {
53 			ret ~= "
54 /**
55  * Macros:
56  *    D_CODE = <pre><code class=\"D\">$0</code></pre>
57  */\n"[1 .. $];
58 	}
59 
60 	ret ~= import("D.d").removeUnicodeBOM;
61 	ret ~= "\n";
62 
63 	import std.algorithm;
64 	import std.range : array;
65 
66 	foreach(e; enums
67 		.map!(a => a.enums)
68 		.joiner
69 		.array
70 		.sort!((a, b) => a.name < b.name)
71 		.uniq!((a, b) => a.name == b.name)) {
72 		if (e.value !is null) {
73 			ret ~= "enum ";
74 			ret ~= e.name;
75 			ret ~= " = ";
76 
77 			if (e.value.length > 2 && e.value[0 .. 2] != "0x")
78 				ret ~= "0x";
79 			ret ~= e.value;
80 			ret ~= "; ///\n";
81 		}
82 	}
83 	ret ~= "\n";
84 
85 	if (containerStruct !is null) {
86 		ret ~= "/// Container for bindings\n";
87 		ret ~= "struct ";
88 		ret ~= containerStruct;
89 		ret ~= " {\n";
90 	}
91 
92 	foreach(grp; enums) {
93 		if (grp.name.length > 0) {
94 			bool haveAnyValid;
95 			foreach(e; grp.enums) {
96 				if (e.value !is null) {
97 					haveAnyValid = true;
98 				}
99 			}
100 			if (!haveAnyValid)
101 				continue;
102 
103 			ret ~= prefixContainer;
104 			ret ~= "\t///\n\t";
105 			
106 			if (grp.isBitmask) {
107 				ret ~= prefixContainer;
108 				ret ~= "@Bitmaskable\n\t";
109 			}
110 			
111 			ret ~= prefixContainer;
112 			ret ~= "enum ";
113 			ret ~= grp.name;
114 			ret ~= " {\n";
115 			
116 			size_t i;
117 			foreach(e; grp.enums) {
118 				if (e.value !is null) {
119 					if (i > 0)
120 						ret ~= ",\n";
121 					
122 					ret ~= prefix;
123 					ret ~= "\t///\n\t";
124 					
125 					ret ~= prefix;
126 					if (e.name.length > 3 && e.name[0 .. 3] == "GL_") {
127 						ret ~= e.name[3 .. $].makeValidCIdentifier;
128 					} else {
129 						ret ~= e.name;
130 					}
131 					
132 					ret ~= " = ";
133 					if (e.value.length > 2 && e.value[0 .. 2] != "0x")
134 						ret ~= "0x";
135 					
136 					ret ~= e.value;
137 					i++;
138 				}
139 			}
140 
141 			ret ~= "\n\t";
142 			ret ~= prefixContainer;
143 			ret ~= "}\n\n";
144 		}
145 	}
146 
147 	if (isStatic) {
148 		ret ~= prefixContainer;
149 		ret ~= "extern(System) {\n";
150 	}
151 
152 	foreach(k, family; functionFamilies) {
153 		foreach(i, func; family.functions) {
154 			string argsSignature;
155 			if (func.argNames.length == 1 && func.argTypes[0] == "void") {
156 			} else {
157 				foreach(j, arg; func.argNames) {
158 					if (j > 0)
159 						argsSignature ~= ", ";
160 
161 					argsSignature ~= func.argTypes[j];
162 					if (arg !is null) {
163 						argsSignature ~= " ";
164 
165 						switch(arg) {
166 							case "ref":
167 								argsSignature ~= "ref_";
168 								break;
169 							case "in":
170 								argsSignature ~= "in_";
171 								break;
172 							case "out":
173 								argsSignature ~= "out_";
174 								break;
175 							default:
176 								argsSignature ~= arg;
177 						}
178 					}
179 				}
180 			}
181 
182 			if (!isStatic) {
183 				ret ~= prefix;
184 				ret ~= "alias fn_";
185 				ret ~= func.name;
186 				ret ~= " = extern(System) ";
187 				ret ~= func.returnType;
188 				ret ~= " function(";
189 				ret ~= argsSignature;
190 				ret ~= ") @system @nogc nothrow;\n";
191 			}
192 
193 			if (i == 0) {
194 				ret ~= "\n";
195 
196 				ret ~= prefix;
197 				ret ~= "/++\n";
198 
199 				ret.genDDOC(family, prefixComment);
200 
201 				ret ~= prefix;
202 				ret ~= " +/\n";
203 			} else {
204 				ret ~= prefix;
205 				ret ~= "/// Ditto\n";
206 			}
207 
208 			OGLIntroducedIn introducedIn = family.introducedIn;
209 			if (func.introducedIn != OGLIntroducedIn.Unknown)
210 				introducedIn = func.introducedIn;
211 
212 			if (introducedIn != OGLIntroducedIn.Unknown) {
213 				ret ~= prefix;
214 				ret ~= "@OpenGL_Version(OGLIntroducedIn.";
215 				ret ~= introducedIn.text;
216 				ret ~= ")\n";
217 			} else {
218 				ret ~= prefix;
219 				ret ~= "@OpenGL_Version(OGLIntroducedIn.Unknown)\n";
220 			}
221 
222 			if (func.introducedInExtension !is null) {
223 				ret ~= prefix;
224 				ret ~= "@OpenGL_Extension(\"";
225 				ret ~= func.introducedInExtension;
226 				ret ~= "\")\n";
227 			}
228 
229 			ret ~= prefix;
230 
231 			if (!isStatic) {
232 				ret ~= "fn_";
233 				ret ~= func.name;
234 				ret ~= " ";
235 				ret ~= func.name;
236 				ret ~= ";\n";
237 			} else {
238 				ret ~= func.returnType;
239 				ret ~= " ";
240 				ret ~= func.name;
241 				ret ~= "(";
242 				ret ~= argsSignature;
243 				ret ~= ") @system @nogc nothrow;\n";
244 			}
245 		}
246 	}
247 
248 	if (isStatic) {
249 		ret ~= prefixContainer;
250 		ret ~= "}\n";
251 	}
252 
253 	if (containerStruct !is null) {
254 		ret ~= "}\n";
255 	}
256 	
257 	import std.file : write;
258 	write(filename, ret);
259 }
260 
261 void genDDOC(T)(ref T ret, OGLFunctionFamily family, string prefix) {
262 	string prefix2 = prefix ~ "    ";
263 	string prefix3 = prefix ~ "                    ";
264 
265 	ret ~= prefix;
266 	ret ~= family.familyOfFunction;
267 	ret ~= ": ";
268 	ret ~= family.fromFilename;
269 	ret ~= "\n";
270 	ret ~= prefix;
271 	ret ~= "\n";
272 	
273 	ret ~= prefix;
274 	ret.genDDOC(family.familyOfFunction, family.docs_description, prefix, prefix);
275 	if (ret[$ - 1] != '\n')
276 		ret ~= "\n";
277 	ret ~= prefix;
278 	ret ~= "\n";
279 	
280 	if (family.docs_notes.value_children.length > 0) {
281 		ret ~= prefix;
282 		ret.genDDOC(family.familyOfFunction, family.docs_notes, prefix, prefix);
283 		if (ret[$ - 1] != '\n')
284 			ret ~= "\n";
285 		ret ~= prefix;
286 		ret ~= "\n";
287 	}
288 	
289 	if (ret.length >= prefix.length * 2 + 2
290 			&& ret[$ - prefix.length - 1 .. $ - 1] == prefix
291 			&& ret[$ - prefix.length * 2 - 2 .. $ - prefix.length - 2] == prefix)
292 		// 2 empty lines here already, no need for another one
293 		ret.length--;
294 	else
295 		ret ~= prefix;
296 	ret ~= "Params:\n";
297 
298 	size_t longestParam = 0;
299 	foreach(ref param; family.docs_parameters) {
300 		size_t length = 1;
301 		foreach(i, name; param.appliesToNames) {
302 			if (i > 0) {
303 				length += ", ".length;
304 			}
305 
306 			if (name == "ref")
307 				length += "ref_".length;
308 			else
309 				length += name.length;
310 		}
311 		if (length > longestParam)
312 			longestParam = length;
313 	}
314 
315 	foreach(ref param; family.docs_parameters) {
316 		ret ~= prefix2;
317 
318 		size_t length = 0;
319 		foreach(i, name; param.appliesToNames) {
320 			if (i > 0) {
321 				ret ~= ", ";
322 				length += ", ".length;
323 			}
324 
325 			if (name == "ref") {
326 				ret ~= "ref_";
327 				length += "ref_".length;
328 			} else {
329 				ret ~= name;
330 				length += name.length;
331 			}
332 		}
333 
334 		for (size_t i = 0; i < longestParam - length; i++)
335 			ret ~= ' ';
336 		ret ~= "= ";
337 		ret.genDDOC(family.familyOfFunction, param.documentation, "", prefix3);
338 		if (ret[$ - 1] != '\n')
339 			ret ~= "\n";
340 	}
341 
342 	ret ~= prefix;
343 	ret ~= "\n";
344 
345 	ret ~= prefix;
346 	ret ~= "Copyright:\n";
347 	ret ~= prefix2;
348 	ret.genDDOC(family.familyOfFunction, family.docs_copyright, prefix2, prefix2);
349 	if (ret[$ - 1] != '\n')
350 		ret ~= "\n";
351 	ret ~= prefix;
352 	ret ~= "\n";
353 
354 	ret ~= prefix;
355 	ret ~= "See_Also:\n";
356 	ret ~= prefix2;
357 	ret.genDDOC(family.familyOfFunction, family.docs_seealso, prefix2, prefix2);
358 	if (ret[$ - 1] != '\n')
359 		ret ~= "\n";
360 }
361 
362 void genDDOC(T)(ref T ret, string functionFamily, ref OGLDocumentation ctx, string linetabs, string linetabsNext) {
363 	bool firstText=true;
364 	genDDOC(ret, functionFamily, ctx, linetabs, linetabsNext, firstText);
365 }
366 
367 void genDDOC(T)(ref T ret, string functionFamily, ref OGLDocumentation ctx, string linetabs, string linetabsNext, ref bool firstText, bool inCode = false) {
368 	import std.string : splitLines, strip, stripRight, KeepTerminator;
369 	import std.algorithm : canFind;
370 	with(ctx) {
371 		string suffix;
372 		string macroPrefix, htmlTag;
373 		bool startCodeBlock = false;
374 		
375 		switch(type) {
376 			case OGLDocumentationType.LookupParameter:
377 				macroPrefix = "D_INLINECODE";
378 				goto case OGLDocumentationType.Container;
379 			case OGLDocumentationType.LookupConstant:
380 				macroPrefix = "D_INLINECODE";
381 				goto case OGLDocumentationType.Container;
382 			case OGLDocumentationType.LookupFunction:
383 				macroPrefix = "D_INLINECODE";
384 				goto case OGLDocumentationType.Container;
385 			case OGLDocumentationType.Title:
386 				htmlTag = "h3";
387 				goto case OGLDocumentationType.Container;
388 				
389 			case OGLDocumentationType.TableContainer:
390 				import std.conv : text;
391 				suffix = "table>[cols=" ~ value_numcols.text ~ "]";
392 				goto case OGLDocumentationType.Container;
393 			case OGLDocumentationType.TableHeader:
394 				suffix = "head";
395 				goto case OGLDocumentationType.Container;
396 			case OGLDocumentationType.TableBody:
397 				suffix = "body";
398 				goto case OGLDocumentationType.Container;
399 			case OGLDocumentationType.TableRow:
400 				suffix = "row";
401 				goto case OGLDocumentationType.Container;
402 			case OGLDocumentationType.TableEntry:
403 				suffix = "entry";
404 				goto case OGLDocumentationType.Container;
405 			case OGLDocumentationType.Copyright:
406 				ret ~= "&copy;";
407 				return;
408 			case OGLDocumentationType.Trademark:
409 				ret ~= "&trade;";
410 				return;
411 			case OGLDocumentationType.StyleContainer:
412 				if (value_string == "bold") {
413 					macroPrefix = "B";
414 				}
415 				goto case OGLDocumentationType.Container;
416 			case OGLDocumentationType.StyleCode:
417 				macroPrefix = "D_CODE";
418 				foreach(child; value_children) {
419 					// only start blocks for multiline code
420 					startCodeBlock = startCodeBlock || child.value_string.canFind('\n');
421 				}
422 				if (!startCodeBlock)
423 					macroPrefix = "D_INLINECODE";
424 				goto case OGLDocumentationType.Container;
425 			case OGLDocumentationType.Footnote:
426 				suffix = "\\/footnote";
427 				goto case OGLDocumentationType.Container;
428 			case OGLDocumentationType.InlineEquation:
429 				suffix = "|<equation";
430 				goto case OGLDocumentationType.Container;
431 				
432 			case OGLDocumentationType.MathMLContainer:
433 				suffix = "MathML[_]";
434 				goto case OGLDocumentationType.Container;
435 			case OGLDocumentationType.MathML_MI:
436 				suffix = "MathML:mi";
437 				goto case OGLDocumentationType.Container;
438 			case OGLDocumentationType.MathML_mn:
439 				suffix = "MathML:mn";
440 				goto case OGLDocumentationType.Container;
441 			case OGLDocumentationType.MathML_mrow:
442 				suffix = "MathML:mrow";
443 				goto case OGLDocumentationType.Container;
444 			case OGLDocumentationType.MathML_msup:
445 				suffix = "MathML:msup";
446 				goto case OGLDocumentationType.Container;
447 			case OGLDocumentationType.MathML_mo:
448 				suffix = "MathML:mo";
449 				goto case OGLDocumentationType.Container;
450 			case OGLDocumentationType.MathML_msub:
451 				suffix = "MathML:msub";
452 				goto case OGLDocumentationType.Container;
453 			case OGLDocumentationType.MathML_mfrac:
454 				suffix = "MathML:mfrac";
455 				goto case OGLDocumentationType.Container;
456 			case OGLDocumentationType.MathML_mtable:
457 				suffix = "MathML:mtable[" ~ value_string ~ "]";
458 				goto case OGLDocumentationType.Container;
459 			case OGLDocumentationType.MathML_mtr:
460 				suffix = "MathML:mtr";
461 				goto case OGLDocumentationType.Container;
462 			case OGLDocumentationType.MathML_mtd:
463 				suffix = "MathML:mtd[" ~ value_string ~ "]";
464 				goto case OGLDocumentationType.Container;
465 			case OGLDocumentationType.MathML_mspace:
466 				suffix = "MathML:mspace[ " ~ value_string ~ " ]";
467 				goto case OGLDocumentationType.Container;
468 			case OGLDocumentationType.MathML_mtext:
469 				suffix = "MathML:mtext[ " ~ value_string ~ " ]";
470 				goto case OGLDocumentationType.Container;
471 			case OGLDocumentationType.MathML_apply:
472 				suffix = "MathML:apply[ " ~ value_string ~ " ]";
473 				goto case OGLDocumentationType.Container;
474 			case OGLDocumentationType.MathML_mover:
475 				suffix = "MathML:mover";
476 				goto case OGLDocumentationType.Container;
477 			case OGLDocumentationType.MathML_munderover:
478 				suffix = "MathML:munderover";
479 				goto case OGLDocumentationType.Container;
480 			case OGLDocumentationType.MathML_msqrt:
481 				suffix = "MathML:msqrt";
482 				goto case OGLDocumentationType.Container;
483 
484 			case OGLDocumentationType.IndexList:
485 				macroPrefix = "OL";
486 				goto case OGLDocumentationType.Container;
487 			case OGLDocumentationType.IndexItem:
488 				macroPrefix = "LI";
489 				goto case OGLDocumentationType.Container;
490 				
491 			case OGLDocumentationType.Link:
492 				macroPrefix = "LINK2 " ~ value_string ~ ",";
493 				goto case OGLDocumentationType.Container;
494 			case OGLDocumentationType.StyleSuperScript:
495 				htmlTag = "sup";
496 				goto case OGLDocumentationType.Container;
497 			case OGLDocumentationType.StyleSubScript:
498 				htmlTag = "sub";
499 				goto case OGLDocumentationType.Container;
500 				
501 			case OGLDocumentationType.Paragraph:
502 				goto case OGLDocumentationType.Container;
503 				
504 			case OGLDocumentationType.Container:
505 				if (startCodeBlock && ret.length >= 1 && ret[$ - 1] != '\n')
506 					ret ~= "\n" ~ linetabs ~ "\n" ~ linetabs;
507 
508 				size_t preMacroPos = -1;
509 				if (startCodeBlock) {
510 					ret ~= "---\n" ~ linetabs;
511 				} else if (macroPrefix !is null) {
512 					preMacroPos = ret.length;
513 					ret ~= "$(";
514 					ret ~= macroPrefix;
515 					ret ~= " ";
516 				} else if (htmlTag !is null) {
517 					ret ~= "<";
518 					ret ~= htmlTag;
519 					ret ~= ">";
520 				}
521 				
522 				size_t codePos = ret.length;
523 				foreach(i, child; value_children)
524 					ret.genDDOC(functionFamily, child, linetabs, linetabsNext, firstText, startCodeBlock);
525 				if (codePos < ret.length && ret[codePos] == ' ' && preMacroPos != -1) {
526 					// space inside macro, remove space and place it before macro
527 					for (size_t i = codePos - 1; i >= preMacroPos; i--)
528 						ret[i] = ret[i - 1];
529 					ret[preMacroPos] = ' ';
530 				}
531 				
532 				if (startCodeBlock) {
533 					if (ret.length >= linetabs.length && ret[$ - linetabs.length .. $] == linetabs)
534 						ret ~= "---\n" ~ linetabs;
535 					else {
536 						if (ret.length && ret[$ - 1] != '\n')
537 							ret ~= '\n' ~ linetabs;
538 						ret ~= "---\n" ~ linetabs;
539 					}
540 				} else if (macroPrefix !is null) {
541 					if (macroPrefix == "D_INLINECODE" && ret.length && ret[$ - 1] == '\n')
542 						ret[$ - 1] = ')';
543 					else
544 						ret ~= ")";
545 				} else if (htmlTag !is null) {
546 					ret ~= "</";
547 					ret ~= htmlTag;
548 					ret ~= ">";
549 				}
550 				return;
551 				
552 			case OGLDocumentationType.Text:
553 				if (inCode) {
554 					foreach(line; value_string.splitLines(KeepTerminator.yes)) {
555 						if (line.length >= 4 && line[0 .. 4] == "    ")
556 							ret ~= line[4 .. $];
557 						else
558 							ret ~= line;
559 						firstText = false;
560 						if (ret.length > 0 && ret[$ - 1] == '\n')
561 							ret ~= linetabs;
562 					}
563 				} else {
564 					size_t i;
565 					string lines = value_string;
566 					
567 					string linesOld = lines;
568 					lines = lines
569 						.replace("NULL", "null")
570 						.replace("== null", "is null");
571 					
572 					foreach(line; lines.splitLines) {
573 						string lineStripped = line.strip;
574 						if (lineStripped.length > 0) {
575 							if (!firstText && !(line[0] == '.' || line[0] == ',' || line[0] == ';')) {
576 								ret ~= " ";
577 							}
578 							
579 							ret ~= lineStripped;
580 							i++;
581 							firstText = false;
582 							
583 							if (linesOld != lines) {
584 								ret ~= "\n" ~ linetabs;
585 							}
586 						}
587 					}
588 				}
589 				return;
590 			case OGLDocumentationType.MathML_mfenced:
591 				foreach(i, child; value_children)
592 					ret.genDDOC(functionFamily, child, linetabsNext, linetabsNext, firstText);
593 				return;
594 			case OGLDocumentationType.MathML_floor:
595 				ret ~= "<mml:floor/>";
596 				return;
597 			case OGLDocumentationType.MathML_infinity:
598 				ret ~= "&#8734;";
599 				return;
600 				
601 			case OGLDocumentationType.Unknown:
602 			default:
603 				// DO NOTHING!!!!!
604 				return;
605 				
606 		}
607 	}
608 }